package com.adopapa.nezhacloud.authc.controller;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import javax.imageio.ImageIO;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.adopapa.nezhacloud.authc.conf.AuthcConstants;
import com.adopapa.nezhacloud.authc.domain.LoginUser;
import com.adopapa.nezhacloud.authc.domain.ResourceInfo;
import com.adopapa.nezhacloud.authc.domain.RoleInfo;
import com.adopapa.nezhacloud.authc.domain.UserInfo;
import com.adopapa.nezhacloud.authc.service.RoleInfoService;
import com.adopapa.nezhacloud.authc.service.SystemDataService;
import com.adopapa.nezhacloud.authc.service.TokenService;
import com.adopapa.nezhacloud.authc.service.UserInfoService;
import com.adopapa.nezhacloud.authc.utils.JwtUtils;
import com.adopapa.nezhacloud.authc.utils.SecurityUtils;
import com.adopapa.nezhacloud.common.DataType;
import com.adopapa.nezhacloud.common.RestResponse;
import com.adopapa.nezhacloud.common.SecurityConstants;
import com.adopapa.nezhacloud.common.Tools;
import com.adopapa.nezhacloud.ddd.controller.BaseController;
import com.adopapa.nezhacloud.redis.RedisService;
import com.google.code.kaptcha.Producer;

import io.jsonwebtoken.Claims;
import io.swagger.v3.oas.annotations.Hidden;
import jakarta.servlet.http.HttpServletRequest;

@RestController
@RequestMapping("/authc/login")
public class LoginController extends BaseController {

	@Autowired
	private Producer captchaProducer;
	@Autowired
	private PasswordEncoder passwordEncoder;
	@Autowired
	private RedisService redisService;
	@Autowired
	private TokenService tokenService;
	@Autowired
	private UserInfoService userInfoService;
	@Autowired
	private SystemDataService systemDataService;

	@GetMapping(path = "/kaptcha.jpg", produces = MediaType.APPLICATION_JSON_VALUE)
	public RestResponse getKaptcha() {
		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			String capText = captchaProducer.createText();
			logger.info(capText);

			RedisTemplate<String, Object> redisTemplate = //
					redisService.getRedisTemplate(AuthcConstants.AUTHC_REDIS_DBINDEX);
			redisTemplate.opsForValue().set(JwtUtils.getValidCodeKey(capText), capText, 300L, TimeUnit.SECONDS);

			BufferedImage bi = captchaProducer.createImage(capText);
			ImageIO.write(bi, "jpg", baos);

			String imageData = Base64.getEncoder().encodeToString(baos.toByteArray());
			imageData = DataType.IMAGE_JPG_BASE64.getValue().concat(imageData);

			baos.close();

			return RestResponse.ok(imageData);

		} catch (Exception e) {
			logger.error(ExceptionUtils.getStackTrace(e));
			return RestResponse.error(e);
		}

	}

	/**
	 * 权限验证
	 */
	@Hidden
	@GetMapping(path = "/auth/{token}", produces = MediaType.APPLICATION_JSON_VALUE)
	public RestResponse auth(@PathVariable String token, @RequestParam String accessUrl) {

		Claims claims = JwtUtils.parseToken(token);
		if (Tools.empty(claims)) {
			return RestResponse.noauth("令牌已过期或验证不正确！");
		}
		RedisTemplate<String, Object> redisTemplate = redisService.getRedisTemplate(AuthcConstants.AUTHC_REDIS_DBINDEX);
		String userkey = JwtUtils.getUserKey(claims);
		boolean islogin = redisTemplate.hasKey(JwtUtils.getTokenKey(userkey));
		if (!islogin) {
			return RestResponse.noauth("登录状态已过期");
		}
		String userId = JwtUtils.getUserId(claims);
		String userName = JwtUtils.getUserName(claims);
		if (Tools.empty(userId) || Tools.empty(userName)) {
			return RestResponse.noauth("令牌验证失败");
		}
		
		boolean authed = systemDataService.auth(token, accessUrl);
		if (!authed) {
			return RestResponse.noauth("没有访问权限");	
		}

		Map<String, String> result = new HashMap<String, String>();
		result.put(SecurityConstants.USER_KEY, userkey);
		result.put(SecurityConstants.USER_ID, userId);
		result.put(SecurityConstants.USER_NAME, userName);

		return RestResponse.ok("令牌验证成功", result);
	}

	@PostMapping(path = "/login", produces = MediaType.APPLICATION_JSON_VALUE, //
			consumes = MediaType.APPLICATION_JSON_VALUE)
	public RestResponse login(@RequestBody Map<String, String> params, HttpServletRequest request) {
		logger.info(params.toString());

		String loginId = params.get("loginId");
		String loginPwd = params.get("loginPwd");
		String validCode = params.get("validCode");

		if (Tools.empty(loginId) //
				|| Tools.empty(loginPwd)//
				|| Tools.empty(validCode)) {
			return RestResponse.other("登录名、密码或者验证码不能为空");
		}

		RedisTemplate<String, Object> redisTemplate = //
				redisService.getRedisTemplate(AuthcConstants.AUTHC_REDIS_DBINDEX);
		String kaptcha = (String) redisTemplate.opsForValue().get(JwtUtils.getValidCodeKey(validCode));
		if (Tools.empty(kaptcha)) {
			return RestResponse.other("验证码已过期");
		}

		// 与数据库密码进行比对
		UserInfo userInfo = userInfoService.getUserInfoById(loginId);
		if (Tools.empty(userInfo)) {
			return RestResponse.other("登录名或密码不正确");
		}

		if (!passwordEncoder.matches(loginPwd, userInfo.getLoginPwd())) {
			return RestResponse.other("登录名或密码不正确");
		}
		
		List<RoleInfo> roleInfos = systemDataService.getRoleInfos(userInfo);
		List<ResourceInfo> resourceInfos = systemDataService.getResourceInfos(roleInfos);
		
		List<String> roles = roleInfos.stream().map(RoleInfo::getRoleName).collect(Collectors.toList());
		List<String> resources = resourceInfos.stream().map(ResourceInfo::getResourceUrl).collect(Collectors.toList());
		
		LoginUser loginUser = new LoginUser();
		loginUser.setUserId(userInfo.getId());
		loginUser.setUserName(userInfo.getUserName());
		loginUser.setIpAddr(getIpAddr(request));
		loginUser.setRoles(roles);
		loginUser.setResources(resources);
		// 生成token并缓存
		Map<String, Object> tokenMap = tokenService.createToken(loginUser);

		return RestResponse.ok("登录成功", tokenMap);
	}

	@DeleteMapping(path = "/logout", consumes = MediaType.APPLICATION_JSON_VALUE)
	public RestResponse logout(HttpServletRequest request) {
		String token = SecurityUtils.getToken(request);

		if (!Tools.empty(token)) {
			// 删除用户缓存记录
			String userkey = JwtUtils.getUserKey(token);
			redisService.getRedisTemplate(AuthcConstants.AUTHC_REDIS_DBINDEX).delete(JwtUtils.getTokenKey(userkey));
			// TODO 记录用户退出日志
			String userName = JwtUtils.getUserName(token);
		}
		return RestResponse.ko("注销成功");
	}

	@PostMapping(path = "/refresh", produces = MediaType.APPLICATION_JSON_VALUE)
	public RestResponse refresh(@RequestHeader String authorization) {
		logger.info(authorization);
		LoginUser loginUser = tokenService.getLoginUser(authorization);
		if (!Tools.empty(loginUser)) {
			// 刷新令牌有效期
			tokenService.refreshToken(loginUser);
			return RestResponse.ok();
		}
		return RestResponse.other("令牌已经过期");
	}

	private String getIpAddr(HttpServletRequest request) {
		return request.getRemoteHost();
	}

}
